package distriKub;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ServerThread extends Thread {
	
	List<List<ServerPlayer>> playingGroups = new ArrayList<List<ServerPlayer>>(); // All the currently active groups

	Socket clientSocket = null;
	boolean listeningState = true;
	Database myDB;
	
	String messageReceived;
	
    BufferedReader is;
    PrintStream os;
	
	public ServerThread(Socket clientSocket, List<List<ServerPlayer>> thePlayingGroups){ // Constructor		
		this.clientSocket = clientSocket;
		this.playingGroups = thePlayingGroups;
		
        try{
			is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
	        os = new PrintStream(clientSocket.getOutputStream());
	    } catch (IOException IOExp){
        	IOExp.printStackTrace();
        } // End of catch block
	} // End of constructor
	
	public void run(){
		
		try{
	        while (listeningState==true){
	           	if (clientSocket.isClosed()) // Client disconnected
	           	{
	           		Server.clientsConnected--;
	           		return;
	           	} // End of if block
	           	
	        	messageReceived = is.readLine(); // Reading client's request
	        	
	        	String messageId = "Uninitialized ID"; // Initialization
	    		String theMessage = "Uninitialized Message"; // Initialization
	    		
	    		if (messageReceived.indexOf(ComRules.MessageDelimiter)!=-1){ // The message has a classification
	    			messageId = messageReceived.split(ComRules.MessageDelimiter)[0];
	    			theMessage = messageReceived.split(ComRules.MessageDelimiter)[1];
	    		}
	        	
	    		if(messageId.equalsIgnoreCase(ComRules.AreYouServer)){
	    			os.println(messageReceived.hashCode());
				}
	    		
	    		if (messageId.equals(ComRules.GameOverReport))
	    		{
	        		String[] gameResults = theMessage.split(",");
	        		
	        		// Format : isFromFile,ReportingUserName,Game_Signature,Winned_Id,Winner_Name#OtherPlayerId1,OtherPlayerId2...
	        		
	        		String isFromFile = gameResults[0].toLowerCase();
	        		boolean afterCrash = false;
	        		
	        		if (isFromFile.equals("false")){ // If the report is from file - no need to remove the 'In-Memory' playing group
		        		String reportingUsername = gameResults[1];
		    			playingGroups.remove(FindGroupByUsername(reportingUsername));
	        		}
	        		
	        		else if (isFromFile.equals("true"))
	        		{afterCrash = true;}
	        		
	        		String gameSignature = gameResults[2];
	        		String gameWinnerId = gameResults[3];
	        		String gameWinnerName = gameResults[4];
	        		String[] gameLosers = theMessage.split("#")[1].split(",");
	        		
	        		// Writing final results into the database
	        		myDB.registerGameResults(afterCrash, gameSignature, gameWinnerId, gameWinnerName, gameLosers);
	        		// Acknowledging the results were registered (so that the player could delete if necessary) 
	        		os.println(ComRules.GameOverReport + ComRules.MessageDelimiter + ComRules.OKRegisteredResults);
	        		
	    		}
	        	
	    		else if (messageId.equals(ComRules.comTerminationString)){
	        		System.out.println("Setting listenning state to : false...");
	        		this.listeningState = false;
	        		os.println(ComRules.DBOK); // Response : OK closed the communication listening
	        	} // End of if block
	        	
	    		else if (messageId.equals(ComRules.RegisterNewPlayer)){
	    			
	    			os.println(theMessage.hashCode()); // Send an "Ack"
	    			
	        		// Signature : (Id,UserName,Password)
	        		
	        		String[] requestParameters = theMessage.split(",");
	        		String playerID = requestParameters[0];
	        		String screenName = requestParameters[1];
	        		String password = requestParameters[2];
	        		
	        		boolean requestResult;
	        		requestResult = myDB.registerNewPlayer(playerID, screenName, password);
	        		
	        		if (requestResult==true)
	        			os.println(ComRules.DBOKRegistered + ComRules.MessageDelimiter + "Nothing");
	        		else
	        			os.println(ComRules.DBERRORRegistered + ComRules.MessageDelimiter + "Nothing");
	        	}
	        	
	    		else if (messageId.equals(ComRules.AddMeToGroup))
	        	{
	        		String[] requestParams = theMessage.split(",");
	        		String screenName = requestParams[0];
	        		String password = requestParams[1];
	        		boolean isValid = myDB.Login(screenName, password);
	        		if (isValid)
	        		{
	        			ServerPlayer player = new ServerPlayer(os, screenName,clientSocket.getInetAddress());
	        			//add to group in server
	        			AddToPlayingGroup(player);
	        			
	        			//return confirmation to client
	        			os.println(ComRules.OKAddedToGroup);
	        		}
	        		else
	        		{
	        			os.println(ComRules.LoginIncorrect);
	        		}
	        	}
	        	
	    		else if (messageId.equals(ComRules.IWannaStart))
	        	{
	        		String[] requestParams = theMessage.split(",");
	        		String username = requestParams[0];
	        		List<ServerPlayer> playersInGroup = FindGroupByUsername(username);
	        		if (playersInGroup.size()<2){
	        			os.println(ComRules.NotEnoughPlayersToStartGame+ComRules.MessageDelimiter+"Nothing");
	        		}
	        		else
	        		{
	        			for (ServerPlayer player : playersInGroup) { // Format : Id#screenName#IpAddress,Id#screenName#IpAddress,...
	        				player.getOutputStream().println(ComRules.PlayerListInGroup+ComRules.MessageDelimiter+GetPlayersListInGroup(playersInGroup));
							player.getOutputStream().println(ComRules.BeginGame+ComRules.MessageDelimiter+"Nothing");
						}
	        		}
	        	}
	        	
	    		else if (messageId.equals(ComRules.RequestPlayersListInMyGroup))
	        	{
	        		String username = theMessage;
	        		List<ServerPlayer> playersInGroup = FindGroupByUsername(username);
	        		os.println(ComRules.PlayerListInGroup+ComRules.MessageDelimiter+GetPlayersListInGroup(playersInGroup));
	        	}
	        	
	        } // End of while block
        
		} // End of try block
		
		catch (IOException IOExp){
			System.out.println("===> ServerThread.java ===> run() -- IOException was thrown... \n");
			IOExp.printStackTrace();
		}
		
	} // End of run method
	
	
	
	private String GetPlayersListInGroup(List<ServerPlayer> playersInGroup) {
		List<String> playersInGroupStrings = new ArrayList<String>();
		for (Player player : playersInGroup) {
			playersInGroupStrings.add(player.getId() + "#" +player.getScreenName() + "#" +player.getIpAddress().getHostAddress());
		}
		return UtilityClass.join(",",(String[])playersInGroupStrings.toArray());
	}

	private List<ServerPlayer> FindGroupByUsername(String username) {
		for (List<ServerPlayer> group: playingGroups)
		{
			for (Player player : group)
			{
				if (player.getScreenName().equals(username))
					return group;
			}
		}
		return null;
	}

	private void AddToPlayingGroup(ServerPlayer player) {
		for (List<ServerPlayer> group: playingGroups)
		{
			if (group.size() <= Server.MaxPlayersInTeam)
			{
				group.add(player);
			}
		}
		ArrayList<ServerPlayer> newGroup = new ArrayList<ServerPlayer>();
		newGroup.add(player);
		playingGroups.add(newGroup);
	}

	public void bindToDatabase(Database theDB){
		this.myDB = theDB;
	}
}